#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: ZhengShiQi
@file: page_object.py
@time: 2025/8/27 17:06
@desc: 页面元素封装
"""

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import time


class NewPageElement:
    """页面元素类，封装了页面元素的定位、等待和操作逻辑"""
    
    # 定位类型映射
    BY_MAPPING = {
        'id': By.ID,
        'xpath': By.XPATH,
        'link_text': By.LINK_TEXT,
        'partial_link_text': By.PARTIAL_LINK_TEXT,
        'name': By.NAME,
        'tag_name': By.TAG_NAME,
        'class_name': By.CLASS_NAME,
        'css_selector': By.CSS_SELECTOR
    }
    
    # 等待条件映射
    WAIT_CONDITION = {
        "presence_of_element_located": ec.presence_of_element_located,
        "element_to_be_clickable": ec.element_to_be_clickable,
        "visibility_of": ec.visibility_of_element_located,
        "invisible": ec.invisibility_of_element_located,
        "element_located_to_be_selected": ec.element_located_to_be_selected,
        "title_is": ec.title_is,
        "title_contains": ec.title_contains,
        # 验证页面显示的文本内容
        "text_present": ec.text_to_be_present_in_element,
        # 验证输入框的输入值或默认值
        "value_present": ec.text_to_be_present_in_element_value,
    }
    
    def __init__(self, by, value, description='', wait_condition='presence_of_element_located', wait_time=10, highlight=False, delay=0):
        """初始化页面元素"""
        self.by = by
        self.value = value
        self.description = description
        self.wait_condition = wait_condition
        self.wait_time = wait_time
        self.highlight = highlight
        self.delay = delay
    
    def __get__(self, instance, owner):
        """获取元素实例"""
        if instance is None:
            return self
        
        # 延迟操作
        if self.delay > 0:
            time.sleep(self.delay)
        
        # 查找元素
        element = self.find_element(instance.driver, self.by, self.value, self.wait_condition, self.wait_time)
        
        # 高亮元素
        if self.highlight:
            self.highlight_element(instance.driver, element)
        
        return ElementWrapper(element, instance.driver)
    
    def __set__(self, instance, value):
        """设置元素值"""
        element = self.__get__(instance, instance.__class__)
        element.clear()
        element.send_keys(value)
    
    @staticmethod
    def get_by(by_type):
        """获取定位类型"""
        return NewPageElement.BY_MAPPING.get(by_type, By.ID)
    
    @staticmethod
    def find_element(driver, by, value, wait_condition='presence', wait_time=10):
        """查找元素"""
        try:
            wait = WebDriverWait(driver, wait_time)
            locator = (NewPageElement.get_by(by), value)
            condition_func = NewPageElement.WAIT_CONDITION.get(wait_condition, ec.presence_of_element_located)
            
            # 处理需要额外参数的等待条件
            if wait_condition in ['text_present']:
                element = wait.until(condition_func(locator, ""))  # 这里需要实际的文本参数
            else:
                element = wait.until(condition_func(locator))
            
            return element
        except (TimeoutException, NoSuchElementException) as e:
            raise e
    
    @staticmethod
    def highlight_element(driver, element):
        """高亮元素"""
        try:
            driver.execute_script("arguments[0].style.border='3px solid purple'", element)
            time.sleep(0.5)
            driver.execute_script("arguments[0].style.border=''", element)
        except Exception as e:
            print(f"高亮元素时出错: {e}")
    
    def get_wait_condition(self, condition_name):
        """获取等待条件函数"""
        condition_func = self.WAIT_CONDITION.get(condition_name)
        if not condition_func:
            raise ValueError(f"无效的等待条件: {condition_name}")
        return condition_func


class ElementWrapper:
    """元素包装器，提供额外的元素操作方法"""
    
    def __init__(self, element, driver):
        self.element = element
        self.driver = driver
    
    def send_keys(self, text):
        """输入文本，如果普通输入失败则尝试使用JavaScript"""
        try:
            self.element.clear()
            self.element.send_keys(text)
        except Exception as e:
            print(f"普通输入失败: {e}，尝试使用JavaScript输入")
            try:
                self.driver.execute_script("arguments[0].value = arguments[1]", self.element, text)
            except Exception as js_e:
                print(f"JavaScript输入也失败了: {js_e}")
                raise js_e
    
    def run_script(self, script, *args):
        """执行JavaScript脚本"""
        return self.driver.execute_script(script, self.element, *args)
    
    def __getattr__(self, name):
        """代理所有其他方法到原始元素"""
        return getattr(self.element, name)